package com.yss;

import com.yss.adapter.AdapterExtraInfo;
import com.yss.adapter.IAdapter;
import com.yss.script.ScriptManager;
import com.yss.threadpool.ThreadPoolFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;

@Slf4j
public abstract class RouterServer {

    private ThreadPoolExecutor threadPool;
    private ScriptManager scriptManager;

    public RouterServer() {
        scriptManager = new ScriptManager();
        threadPool = ThreadPoolFactory.getInstance();
    }

    public abstract List<Object> forwarding(Object params);

    public List<Object> commonHandle(Map<String, List<IAdapter>> mappers, Object params) {
        List<IAdapter> adapters = new LinkedList<>();
        List<Object> result = new LinkedList<>();

        for (Map.Entry<String, List<IAdapter>> entry : mappers.entrySet()) {
            if ((Boolean) scriptManager.compileAndExecute(entry.getKey(), params)) {
                adapters.addAll(entry.getValue());
            }
        }


        if (CollectionUtils.isEmpty(adapters)) return result;

        CountDownLatch latch = new CountDownLatch(adapters.size());

        List<? extends Future<?>> futures = adapters.stream().map(iAdapter -> threadPool.submit(() -> {
            Object adapterResult = null;
            try {
                AdapterExtraInfo adapterExtraInfo = AdapterExtraInfo.builder().params(params).build();
                adapterResult = iAdapter.handle(adapterExtraInfo);
            } catch (RuntimeException exp) {
                log.error("\nRouter Server execute adapter handler failed , The cause is : {}", exp);
            } finally {
                latch.countDown();
            }
            return adapterResult;
        })).collect(Collectors.toList());

        try {
            latch.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        for (Future<?> future : futures) {
            try {
                result.add(future.get());
            } catch (InterruptedException | ExecutionException e) {
                Thread.currentThread().interrupt();
            }
        }

        return result;
    }

}
